﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
//using Image;

namespace GeneticAlgorithm
{
    // Текущее приближение изображения
    public class Workarea : ICloneable
    {      
        // Все полигоны текущего приближения
        public List<Polygon> Polygons { get; set; }

        // Переменная для проверки: изменилось ли текущее приближение?
        public bool IsChange { get; set; }

        // Определяем текущее приближение случайным образом
        public void SetRandom()
        {
            Polygons = new List<Polygon>();

            // Добавляем минимальное количество случайных полигонов
            for (int i = 0; i < Constants.MinPolygons; i++)
            {
                AddPolygon();
            }

            IsChange = true;
        }

        // Применить мутацию для текущего приближения изображения
        public void Mutate()
        {
            // Добавляем новый случайный полигон с определённой вероятностью
            if (Constants.Rand.NextDouble() <= Constants.MutationWorkareaAddPolygonChance)
            {
                AddPolygon();
                IsChange = true;
            }

            // Удаляем полигон с определённой вероятностью
            if (Constants.Rand.NextDouble() <= Constants.MutationWorkareaDelPolygonChance)
            {
                DelPolygon();
                IsChange = true;
            }

            // Проводим мутацию каждого полигона
            Polygons.ForEach(p => p.Mutate(this));
        }

        // Добавить новый случайный полигон
        private void AddPolygon()
        {
            Polygon polygon;

            if (Polygons.Count < Constants.MaxPolygons)
            {
                polygon = new Polygon();
                polygon.SetRandom();
                Polygons.Add(polygon);
            }
        }

        // Удалить полигон
        private void DelPolygon()
        {
            // Проверяем, что при удалении полигонов останется не меньше определённого порога
            if (Polygons.Count > Constants.MinPolygons)
            {
                // Случайным образом определяем номер полигона
                int index = Constants.Rand.Next(0, Polygons.Count);
                Polygons.RemoveAt(index);
            }
        }

        // Считать фитнес функцию
        public double Fitness(Color[,] colors)
        {
            double fitness = 0;

            // Создаём Bitmap для текущего приближения изображения
            Bitmap img = Draw();

            

            // Для каждого пикселя считаем сумму погрешностей всех цветов
            for (int i = 0; i < Constants.Width; i++)
            {
                for (int j = 0; j < Constants.Height; j++)
                {
                    // Цвет пикселя приближения
                    Color c1 = img.GetPixel(i, j);                    
                    // Цвет пикселя изображения
                    Color c2 = colors[i, j];

                    int r = c1.R - c2.R;
                    int g = c1.G - c2.G;
                    int b = c1.B - c2.B;
                    fitness += r * r + g * g + b * b;
                }
            }
            
            // Удаляем Bitmap 
            img.Dispose();

            return fitness;
        }

        // Создать Bitmap для текущего приближения изображения
        public Bitmap Draw()
        {
            // Создаём Bitmap размеров изображения
            Bitmap img = new Bitmap(Constants.Width, Constants.Height);

            using (Graphics g = Graphics.FromImage(img))
            {
                // Устанавливаем высокое качество
                g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;

                // Делаем чёрный фон
                g.Clear(Color.Black);

                // Добавляем полигоны на Bitmap
                Polygons.ForEach(p => p.Draw(g));
            }

            return img;
        }

        // Клонировать текущее приближение
        public object Clone()
        {
            Workarea newarea = new Workarea();

            newarea.Polygons = new List<Polygon>();
            Polygons.ForEach(p => newarea.Polygons.Add(p.Clone() as Polygon));

            return newarea;
        }
    }
}
